{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python Algorithmic Trading Cookbook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Chapter 8: Algorithmic Trading: Code Strategies Step-by-step"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This Jupyter Notebook is created using Python version 3.8.2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Requirements"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can install the requirements for this Jupyter Notebook by executing the below cell"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install pyalgotrading pyalgostrategypool TA-Lib"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you get an error while installing TA-Lib, please refer to the book for installation instructions."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Master Recipe\n",
    "\n",
    "The following code will help you set up the broker connection with Zerodha, which will be used by all the recipes in this chapter. Please make sure you have followed these steps before trying out any recipe. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyalgotrading.strategy.strategy_base import StrategyBase\n",
    "from pyalgotrading.constants import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 1: EMA-Regular-Order Strategy: Coding the '__init__', '\\_\\_initialize\\_\\_', 'name' and 'versions_supported' methods"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyEMARegularOrder(StrategyBase):\n",
    "    \n",
    "    def __init__(self, *args, **kwargs):\n",
    "        super().__init__(*args, **kwargs)\n",
    "\n",
    "        self.timeperiod1 = self.strategy_parameters['timeperiod1']\n",
    "        self.timeperiod2 = self.strategy_parameters['timeperiod2']\n",
    "\n",
    "        self.main_order = None\n",
    "\n",
    "    def initialize(self):\n",
    "        self.main_order = {}\n",
    "    \n",
    "    @staticmethod\n",
    "    def name():\n",
    "        return 'EMA Regular Order Strategy'\n",
    "\n",
    "    @staticmethod    \n",
    "    def versions_supported():\n",
    "        return AlgoBullsEngineVersion.VERSION_3_2_0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 2: EMA-Regular-Order Strategy: Coding the 'strategy_select_instruments_for_entry' method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyEMARegularOrder(StrategyBase):\n",
    "   \n",
    "    # Previous methods not shown\n",
    "\n",
    "    def get_crossover_value(self, instrument):\n",
    "        hist_data = self.get_historical_data(instrument)\n",
    "        ema_x = talib.EMA(hist_data['close'], timeperiod=self.timeperiod1)\n",
    "        ema_y = talib.EMA(hist_data['close'], timeperiod=self.timeperiod2)\n",
    "        crossover_value = self.utils.crossover(ema_x, ema_y)\n",
    "        return crossover_value\n",
    "\n",
    "    def strategy_select_instruments_for_entry(self, candle, instruments_bucket):\n",
    "\n",
    "        selected_instruments_bucket = []\n",
    "        sideband_info_bucket = []\n",
    "\n",
    "        for instrument in instruments_bucket:\n",
    "            crossover_value = self.get_crossover_value(instrument)\n",
    "            if crossover_value == 1:\n",
    "                selected_instruments_bucket.append(instrument)\n",
    "                sideband_info_bucket.append({'action': 'BUY'})\n",
    "            elif crossover_value == -1:\n",
    "                if self.strategy_mode is StrategyMode.INTRADAY:\n",
    "                    selected_instruments_bucket.append(instrument)\n",
    "                    sideband_info_bucket.append({'action': 'SELL'})\n",
    "                    \n",
    "        return selected_instruments_bucket, sideband_info_bucket"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 3: EMA-Regular-Order Strategy: Coding the 'strategy_enter_position' method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyEMARegularOrder(StrategyBase):\n",
    "   \n",
    "    # Previous methods not shown\n",
    "\n",
    "    def strategy_enter_position(self, candle, instrument, sideband_info):\n",
    "        if sideband_info['action'] == 'BUY':\n",
    "            qty = self.number_of_lots * instrument.lot_size\n",
    "            self.main_order[instrument] = \\\n",
    "                self.broker.BuyOrderRegular(instrument=instrument,\n",
    "                                            order_code=BrokerOrderCodeConstants.INTRADAY,\n",
    "                                            order_variety=BrokerOrderVarietyConstants.MARKET,\n",
    "                                            quantity=qty)\n",
    "        elif sideband_info['action'] == 'SELL':\n",
    "            qty = self.number_of_lots * instrument.lot_size\n",
    "            self.main_order[instrument] = \\\n",
    "                self.broker.SellOrderRegular(instrument=instrument,\n",
    "                                             order_code=BrokerOrderCodeConstants.INTRADAY,\n",
    "                                             order_variety=BrokerOrderVarietyConstants.MARKET,\n",
    "                                             quantity=qty)\n",
    "        else:\n",
    "            raise SystemExit(f'Got invalid sideband_info value: {sideband_info}')\n",
    "\n",
    "        return self.main_order[instrument]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 4: EMA-Regular-Order Strategy: Coding the 'strategy_select_instruments_for_exit' method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyEMARegularOrder(StrategyBase):\n",
    "   \n",
    "    # Previous methods not shown\n",
    "\n",
    "    def strategy_select_instruments_for_exit(self, candle, instruments_bucket):\n",
    "        selected_instruments_bucket = []\n",
    "        sideband_info_bucket = []\n",
    "\n",
    "        for instrument in instruments_bucket:\n",
    "            if self.main_order.get(instrument) is not None:\n",
    "                crossover_value = self.get_crossover_value(instrument)\n",
    "                if crossover_value in [1, -1]:\n",
    "                    selected_instruments_bucket.append(instrument)\n",
    "                    sideband_info_bucket.append({'action': 'EXIT'})\n",
    "        return selected_instruments_bucket, sideband_info_bucket"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 5: EMA-Regular-Order Strategy: Coding the 'strategy_exit_position' method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyEMARegularOrder(StrategyBase):\n",
    "   \n",
    "    # Previous methods not shown\n",
    "\n",
    "    def strategy_exit_position(self, candle, instrument, sideband_info):\n",
    "        if sideband_info['action'] == 'EXIT':\n",
    "            self.main_order[instrument].exit_position()\n",
    "            self.main_order[instrument] = None\n",
    "            return True\n",
    "\n",
    "        return False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 6:  EMA-Regular-Order Strategy: Create strategy on AlgoBulls Trading Platform"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import inspect\n",
    "from pyalgotrading.algobulls import AlgoBullsConnection\n",
    "from pyalgostrategypool.strategy_ema_regular_order import StrategyEMARegularOrder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "algobulls_connection = AlgoBullsConnection()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'https://app.algobulls.com/user/login'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "algobulls_connection.get_authorization_url()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "algobulls_connection.set_access_token('80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "class StrategyEMARegularOrder(StrategyBase):\n",
      "\n",
      "    def __init__(self, *args, **kwargs):\n",
      "        super().__init__(*args, **kwargs)\n",
      "\n",
      "        self.timeperiod1 = self.strategy_parameters['timeperiod1']\n",
      "        self.timeperiod2 = self.strategy_parameters['timeperiod2']\n",
      "\n",
      "        self.main_order = None\n",
      "\n",
      "    def initialize(self):\n",
      "        self.main_order = {}\n",
      "\n",
      "    @staticmethod\n",
      "    def name():\n",
      "        return 'EMA Regular Order Strategy'\n",
      "\n",
      "    @staticmethod\n",
      "    def versions_supported():\n",
      "        return AlgoBullsEngineVersion.VERSION_3_2_0\n",
      "\n",
      "    def get_crossover_value(self, instrument):\n",
      "        hist_data = self.get_historical_data(instrument)\n",
      "        ema_x = talib.EMA(hist_data['close'], timeperiod=self.timeperiod1)\n",
      "        ema_y = talib.EMA(hist_data['close'], timeperiod=self.timeperiod2)\n",
      "        crossover_value = self.utils.crossover(ema_x, ema_y)\n",
      "        return crossover_value\n",
      "\n",
      "    def strategy_select_instruments_for_entry(self, candle, instruments_bucket):\n",
      "\n",
      "        selected_instruments_bucket = []\n",
      "        sideband_info_bucket = []\n",
      "\n",
      "        for instrument in instruments_bucket:\n",
      "            crossover_value = self.get_crossover_value(instrument)\n",
      "            if crossover_value == 1:\n",
      "                selected_instruments_bucket.append(instrument)\n",
      "                sideband_info_bucket.append({'action': 'BUY'})\n",
      "            elif crossover_value == -1:\n",
      "                if self.strategy_mode is StrategyMode.INTRADAY:\n",
      "                    selected_instruments_bucket.append(instrument)\n",
      "                    sideband_info_bucket.append({'action': 'SELL'})\n",
      "\n",
      "        return selected_instruments_bucket, sideband_info_bucket\n",
      "\n",
      "    def strategy_enter_position(self, candle, instrument, sideband_info):\n",
      "        if sideband_info['action'] == 'BUY':\n",
      "            qty = self.number_of_lots * instrument.lot_size\n",
      "            self.main_order[instrument] = \\\n",
      "                self.broker.BuyOrderRegular(instrument=instrument,\n",
      "                                            order_code=BrokerOrderCodeConstants.INTRADAY,\n",
      "                                            order_variety=BrokerOrderVarietyConstants.MARKET,\n",
      "                                            quantity=qty)\n",
      "        elif sideband_info['action'] == 'SELL':\n",
      "            qty = self.number_of_lots * instrument.lot_size\n",
      "            self.main_order[instrument] = \\\n",
      "                self.broker.SellOrderRegular(instrument=instrument,\n",
      "                                             order_code=BrokerOrderCodeConstants.INTRADAY,\n",
      "                                             order_variety=BrokerOrderVarietyConstants.MARKET,\n",
      "                                             quantity=qty)\n",
      "        else:\n",
      "            raise SystemExit(f'Got invalid sideband_info value: {sideband_info}')\n",
      "\n",
      "        return self.main_order[instrument]\n",
      "\n",
      "    def strategy_select_instruments_for_exit(self, candle, instruments_bucket):\n",
      "        selected_instruments_bucket = []\n",
      "        sideband_info_bucket = []\n",
      "\n",
      "        for instrument in instruments_bucket:\n",
      "            if self.main_order.get(instrument) is not None:\n",
      "                crossover_value = self.get_crossover_value(instrument)\n",
      "                if crossover_value in [1, -1]:\n",
      "                    selected_instruments_bucket.append(instrument)\n",
      "                    sideband_info_bucket.append({'action': 'EXIT'})\n",
      "        return selected_instruments_bucket, sideband_info_bucket\n",
      "\n",
      "    def strategy_exit_position(self, candle, instrument, sideband_info):\n",
      "        if sideband_info['action'] == 'EXIT':\n",
      "            self.main_order[instrument].exit_position()\n",
      "            self.main_order[instrument] = None\n",
      "            return True\n",
      "\n",
      "        return False\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(inspect.getsource(StrategyEMARegularOrder))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validating Strategy...\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'data': 'success', 'strategyCode': '49287246f9704bbcbad76ade9e2091d9'}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "algobulls_connection.create_strategy(StrategyEMARegularOrder)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### There's more..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validating Strategy...\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'data': 'success'}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "algobulls_connection.create_strategy(StrategyEMARegularOrder, overwrite=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 7: MACD-Bracket-Order Strategy: Coding the '__init__', '\\_\\_initialize\\_\\_', 'name' and 'versions_supported' methods"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyMACDBracketOrder(StrategyBase):\n",
    "    \n",
    "    def __init__(self, *args, **kwargs):\n",
    "        super().__init__(*args, **kwargs)\n",
    "\n",
    "        self.fastMA_period = self.strategy_parameters['fastma_period']\n",
    "        self.slowMA_period = self.strategy_parameters['slowma_period']\n",
    "        self.signal_period = self.strategy_parameters['signal_period']\n",
    "        self.stoploss = self.strategy_parameters['stoploss_trigger']\n",
    "        self.target = self.strategy_parameters['target_trigger']\n",
    "        self.trailing_stoploss = self.strategy_parameters['trailing_stoploss_trigger']\n",
    "\n",
    "        self.main_order = None\n",
    "\n",
    "    def initialize(self):\n",
    "        self.main_order = {}\n",
    "\n",
    "    @staticmethod\n",
    "    def name():\n",
    "        return 'MACD Bracket Order Strategy'\n",
    "\n",
    "    @staticmethod\n",
    "    def versions_supported():\n",
    "        return VERSION_3_2_0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 8: MACD-Bracket-Order Strategy: Coding the 'strategy_select_instruments_for_entry' method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyMACDBracketOrder(StrategyBase):\n",
    "    \n",
    "    # Note: Some methods are not shown here    \n",
    "    \n",
    "    def get_crossover_value(self, instrument):\n",
    "        hist_data = self.get_historical_data(instrument)\n",
    "        macdline, macdsignal, _ = talib.MACD(hist_data['close'], \n",
    "                                             fastperiod=self.fastMA_period, \n",
    "                                             slowperiod=self.slowMA_period, \n",
    "                                             signalperiod=self.signal_period)\n",
    "        crossover_value = self.utils.crossover(macdline, macdsignal)\n",
    "        return crossover_value\n",
    "\n",
    "    def strategy_select_instruments_for_entry(self, candle, instruments_bucket):\n",
    "\n",
    "        selected_instruments_bucket = []\n",
    "        sideband_info_bucket = []\n",
    "\n",
    "        for instrument in instruments_bucket:\n",
    "            crossover_value = self.get_crossover_value(instrument)\n",
    "            if crossover_value == 1:\n",
    "                selected_instruments_bucket.append(instrument)\n",
    "                sideband_info_bucket.append({'action': 'BUY'})\n",
    "            elif crossover_value == -1:\n",
    "                if self.strategy_mode is StrategyMode.INTRADAY:\n",
    "                    selected_instruments_bucket.append(instrument)\n",
    "                    sideband_info_bucket.append({'action': 'SELL'})\n",
    "\n",
    "        return selected_instruments_bucket, sideband_info_bucket    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 9: MACD-Bracket-Order Strategy: Coding the 'strategy_enter_position' method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyMACDBracketOrder(StrategyBase):\n",
    "    \n",
    "    # Note: Some methods are not shown here \n",
    "    \n",
    "    def strategy_enter_position(self, candle, instrument, sideband_info):\n",
    "        if sideband_info['action'] == 'BUY':\n",
    "            qty = self.number_of_lots * instrument.lot_size\n",
    "            ltp = self.broker.get_ltp(instrument)\n",
    "            self.main_order[instrument] = \\\n",
    "                self.broker.BuyOrderBracket(instrument=instrument,\n",
    "                                            order_code=BrokerOrderCodeConstants.INTRADAY,\n",
    "                                            order_variety=BrokerOrderVarietyConstants.LIMIT,\n",
    "                                            quantity=qty,\n",
    "                                            price=ltp,\n",
    "                                            stoploss_trigger=ltp - (ltp * self.stoploss),\n",
    "                                            target_trigger=ltp + (ltp * self.target),\n",
    "                                            trailing_stoploss_trigger=ltp * self.trailing_stoploss)\n",
    "\n",
    "        elif sideband_info['action'] == 'SELL':\n",
    "            qty = self.number_of_lots * instrument.lot_size\n",
    "            ltp = self.broker.get_ltp(instrument)\n",
    "            self.main_order[instrument] = \\\n",
    "                self.broker.SellOrderBracket(instrument=instrument,\n",
    "                                             order_code=BrokerOrderCodeConstants.INTRADAY,\n",
    "                                             order_variety=BrokerOrderVarietyConstants.LIMIT,\n",
    "                                             quantity=qty,\n",
    "                                             price=ltp,\n",
    "                                             stoploss_trigger=ltp + (ltp * self.stoploss),\n",
    "                                             target_trigger=ltp - (ltp * self.target),\n",
    "                                             trailing_stoploss_trigger=ltp * self.trailing_stoploss)\n",
    "        else:\n",
    "            raise SystemExit(f'Got invalid sideband_info value: {sideband_info}')\n",
    "\n",
    "        return self.main_order[instrument]\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 10: MACD-Bracket-Order Strategy: Coding the 'strategy_select_instruments_for_exit' method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyMACDBracketOrder(StrategyBase):\n",
    "    \n",
    "    # Note: Some methods are not shown here   \n",
    "    \n",
    "    def strategy_select_instruments_for_exit(self, candle, instruments_bucket):\n",
    "        selected_instruments_bucket = []\n",
    "        sideband_info_bucket = []\n",
    "\n",
    "        for instrument in instruments_bucket:\n",
    "            if self.main_order.get(instrument) is not None:\n",
    "                crossover_value = self.get_crossover_value(instrument)\n",
    "                if crossover_value in [1, -1]:\n",
    "                    selected_instruments_bucket.append(instrument)\n",
    "                    sideband_info_bucket.append({'action': 'EXIT'})\n",
    "        return selected_instruments_bucket, sideband_info_bucket    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 11: MACD-Bracket-Order Strategy: Coding the 'strategy_exit_position' method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StrategyMACDBracketOrder(StrategyBase):\n",
    "    \n",
    "    # Note: Some methods are not shown here   \n",
    "    \n",
    "    def strategy_exit_position(self, candle, instrument, sideband_info):\n",
    "        if sideband_info['action'] == 'EXIT':\n",
    "            self.main_order[instrument].exit_position()\n",
    "            self.main_order[instrument] = None\n",
    "            return True\n",
    "\n",
    "        return False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 12: MACD-Bracket-Order Strategy: Create strategy on AlgoBulls Trading Platform"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "import inspect\n",
    "from pyalgostrategypool.strategy_macd_bracket_order import StrategyMACDBracketOrder\n",
    "from pyalgotrading.algobulls import AlgoBullsConnection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "algobulls_connection = AlgoBullsConnection()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'https://app.algobulls.com/user/login'"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "algobulls_connection.get_authorization_url()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "algobulls_connection.set_access_token('80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "class StrategyMACDBracketOrder(StrategyBase):\n",
      "\n",
      "    def __init__(self, *args, **kwargs):\n",
      "        super().__init__(*args, **kwargs)\n",
      "\n",
      "        self.fastMA_period = self.strategy_parameters['fastma_period']\n",
      "        self.slowMA_period = self.strategy_parameters['slowma_period']\n",
      "        self.signal_period = self.strategy_parameters['signal_period']\n",
      "        self.stoploss = self.strategy_parameters['stoploss_trigger']\n",
      "        self.target = self.strategy_parameters['target_trigger']\n",
      "        self.trailing_stoploss = self.strategy_parameters['trailing_stoploss_trigger']\n",
      "\n",
      "        self.main_order = None\n",
      "\n",
      "    def initialize(self):\n",
      "        self.main_order = {}\n",
      "\n",
      "    @staticmethod\n",
      "    def name():\n",
      "        return 'MACD Bracket Order Strategy'\n",
      "\n",
      "    @staticmethod\n",
      "    def versions_supported():\n",
      "        return AlgoBullsEngineVersion.VERSION_3_2_0\n",
      "\n",
      "    def get_crossover_value(self, instrument):\n",
      "        hist_data = self.get_historical_data(instrument)\n",
      "        macdline, macdsignal, _ = talib.MACD(hist_data['close'],\n",
      "                                             fastperiod=self.fastMA_period,\n",
      "                                             slowperiod=self.slowMA_period,\n",
      "                                             signalperiod=self.signal_period)\n",
      "        crossover_value = self.utils.crossover(macdline, macdsignal)\n",
      "        return crossover_value\n",
      "\n",
      "    def strategy_select_instruments_for_entry(self, candle, instruments_bucket):\n",
      "\n",
      "        selected_instruments_bucket = []\n",
      "        sideband_info_bucket = []\n",
      "\n",
      "        for instrument in instruments_bucket:\n",
      "            crossover_value = self.get_crossover_value(instrument)\n",
      "            if crossover_value == 1:\n",
      "                selected_instruments_bucket.append(instrument)\n",
      "                sideband_info_bucket.append({'action': 'BUY'})\n",
      "            elif crossover_value == -1:\n",
      "                if self.strategy_mode is StrategyMode.INTRADAY:\n",
      "                    selected_instruments_bucket.append(instrument)\n",
      "                    sideband_info_bucket.append({'action': 'SELL'})\n",
      "\n",
      "        return selected_instruments_bucket, sideband_info_bucket\n",
      "\n",
      "    def strategy_enter_position(self, candle, instrument, sideband_info):\n",
      "        if sideband_info['action'] == 'BUY':\n",
      "            qty = self.number_of_lots * instrument.lot_size\n",
      "            ltp = self.broker.get_ltp(instrument)\n",
      "            self.main_order[instrument] = \\\n",
      "                self.broker.BuyOrderBracket(instrument=instrument,\n",
      "                                            order_code=BrokerOrderCodeConstants.INTRADAY,\n",
      "                                            order_variety=BrokerOrderVarietyConstants.LIMIT,\n",
      "                                            quantity=qty,\n",
      "                                            price=ltp,\n",
      "                                            stoploss_trigger=ltp - (ltp * self.stoploss),\n",
      "                                            target_trigger=ltp + (ltp * self.target),\n",
      "                                            trailing_stoploss_trigger=ltp * self.trailing_stoploss)\n",
      "\n",
      "        elif sideband_info['action'] == 'SELL':\n",
      "            qty = self.number_of_lots * instrument.lot_size\n",
      "            ltp = self.broker.get_ltp(instrument)\n",
      "            self.main_order[instrument] = \\\n",
      "                self.broker.SellOrderBracket(instrument=instrument,\n",
      "                                             order_code=BrokerOrderCodeConstants.INTRADAY,\n",
      "                                             order_variety=BrokerOrderVarietyConstants.LIMIT,\n",
      "                                             quantity=qty,\n",
      "                                             price=ltp,\n",
      "                                             stoploss_trigger=ltp + (ltp * self.stoploss),\n",
      "                                             target_trigger=ltp - (ltp * self.target),\n",
      "                                             trailing_stoploss_trigger=ltp * self.trailing_stoploss)\n",
      "        else:\n",
      "            raise SystemExit(f'Got invalid sideband_info value: {sideband_info}')\n",
      "\n",
      "        return self.main_order[instrument]\n",
      "\n",
      "    def strategy_select_instruments_for_exit(self, candle, instruments_bucket):\n",
      "        selected_instruments_bucket = []\n",
      "        sideband_info_bucket = []\n",
      "\n",
      "        for instrument in instruments_bucket:\n",
      "            if self.main_order.get(instrument) is not None:\n",
      "                crossover_value = self.get_crossover_value(instrument)\n",
      "                if crossover_value in [1, -1]:\n",
      "                    selected_instruments_bucket.append(instrument)\n",
      "                    sideband_info_bucket.append({'action': 'EXIT'})\n",
      "        return selected_instruments_bucket, sideband_info_bucket\n",
      "\n",
      "    def strategy_exit_position(self, candle, instrument, sideband_info):\n",
      "        if sideband_info['action'] == 'EXIT':\n",
      "            self.main_order[instrument].exit_position()\n",
      "            self.main_order[instrument] = None\n",
      "            return True\n",
      "\n",
      "        return False\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(inspect.getsource(StrategyMACDBracketOrder))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validating Strategy...\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'data': 'success', 'strategyCode': '4faf514fe096432b8e9f80f5951bd2ea'}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "algobulls_connection.create_strategy(StrategyMACDBracketOrder)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### There's more..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validating Strategy...\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'data': 'success'}"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "algobulls_connection.create_strategy(StrategyMACDBracketOrder, overwrite=True)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
